;; Licensed to the .NET Foundation under one or more agreements.
;; The .NET Foundation licenses this file to you under the MIT license.

include AsmOffsets.inc      ; generated by the build from AsmOffsets.cpp

;;
;; MACROS
;;

FASTCALL_FUNC macro FuncName,cbArgs
    FuncNameReal EQU @&FuncName&@&cbArgs
    FuncNameReal proc public
    FuncName label proc
    PUBLIC FuncName

endm

FASTCALL_ENDFUNC macro
    FuncNameReal endp
endm

ALTERNATE_ENTRY macro Name

Name label proc
PUBLIC Name
        endm

__tls_array     equ 2Ch     ;; offsetof(TEB, ThreadLocalStoragePointer)

;;
;; __declspec(thread) version
;;
INLINE_GETTHREAD macro destReg, trashReg
    ASSUME fs : NOTHING
    EXTERN __tls_index : DWORD
    EXTERN _tls_CurrentThread : DWORD

    mov         destReg, [__tls_index]
    mov         trashReg, fs:[__tls_array]
    mov         destReg, [trashReg + destReg * 4]
    add         destReg, SECTIONREL _tls_CurrentThread
endm


INLINE_THREAD_UNHIJACK macro threadReg, trashReg1, trashReg2
        ;;
        ;; Thread::Unhijack()
        ;;
        mov         trashReg1, [threadReg + OFFSETOF__Thread__m_pvHijackedReturnAddress]
        cmp         trashReg1, 0
        je          @F

        mov         trashReg2, [threadReg + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation]
        mov         [trashReg2], trashReg1
        mov         dword ptr [threadReg + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], 0
        mov         dword ptr [threadReg + OFFSETOF__Thread__m_pvHijackedReturnAddress], 0

@@:
endm

;;
;; Macro used from unmanaged helpers called from managed code where the helper does not transition immediately
;; into pre-emptive mode but may cause a GC and thus requires the stack is crawlable. This is typically the
;; case for helpers that meddle in GC state (e.g. allocation helpers) where the code must remain in
;; cooperative mode since it handles object references and internal GC state directly but a garbage collection
;; may be inevitable. In these cases we need to be able to transition to pre-meptive mode deep within the
;; unmanaged code but still be able to initialize the stack iterator at the first stack frame which may hold
;; interesting GC references. In all our helper cases this corresponds to the most recent managed frame (e.g.
;; the helper's caller).
;;
;; This macro builds a frame describing the current state of managed code.
;;
;; The macro assumes it is called from a helper that has already set up an EBP frame and that the values of
;; EBX, ESI and EDI remain unchanged from their values in managed code. It pushes the frame at the top of the
;; stack.
;;
;; EAX is trashed by this macro.
;;
PUSH_COOP_PINVOKE_FRAME macro transitionFrameReg
    lea         eax, [ebp + 8]                      ; get the ESP of the caller
    push        eax                                 ; save ESP
    push        edi
    push        esi
    push        ebx
    push        PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_RSP
    push        eax                                 ; Thread * (unused by stackwalker)
    mov         eax, [ebp + 0]                      ; Find previous EBP value
    push        eax                                 ; save EBP
    mov         eax, [ebp + 4]                      ; Find the return address
    push        eax                                 ; save m_RIP

    lea         transitionFrameReg, [esp + 0]       ; transitionFrameReg == address of frame
endm

;;
;; Remove the frame from a previous call to PUSH_COOP_PINVOKE_FRAME from the top of the stack and restore EBX,
;; ESI and EDI to their previous values.
;;
;; TRASHES ECX
;;
POP_COOP_PINVOKE_FRAME macro
    add         esp, 4*4
    pop         ebx
    pop         esi
    pop         edi
    pop         ecx
endm


;;
;; CONSTANTS -- INTEGER
;;
TSF_Attached                    equ 01h
TSF_SuppressGcStress            equ 08h
TSF_DoNotTriggerGc              equ 10h

;; GC type flags
GC_ALLOC_FINALIZE               equ 1

;; Note: these must match the defs in PInvokeTransitionFrameFlags
PTFF_SAVE_RBX           equ 00000001h
PTFF_SAVE_RSI           equ 00000002h
PTFF_SAVE_RDI           equ 00000004h
PTFF_SAVE_ALL_PRESERVED equ 00000007h   ;; NOTE: RBP is not included in this set!
PTFF_SAVE_RSP           equ 00008000h
PTFF_SAVE_RAX           equ 00000100h   ;; RAX is saved if it contains a GC ref and we're in hijack handler
PTFF_SAVE_ALL_SCRATCH   equ 00000700h
PTFF_RAX_IS_GCREF       equ 00010000h   ;; iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar
PTFF_RAX_IS_BYREF       equ 00020000h   ;; iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar
PTFF_THREAD_ABORT       equ 00100000h   ;; indicates that ThreadAbortException should be thrown when returning from the transition

;; These must match the TrapThreadsFlags enum
TrapThreadsFlags_None            equ 0
TrapThreadsFlags_AbortInProgress equ 1
TrapThreadsFlags_TrapThreads     equ 2

;; This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT
STATUS_REDHAWK_THREAD_ABORT      equ 43h

;;
;; Rename fields of nested structs
;;
OFFSETOF__Thread__m_alloc_context__alloc_ptr        equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr
OFFSETOF__Thread__m_alloc_context__alloc_limit      equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit

;;
;; CONSTANTS -- SYMBOLS
;;

RhDebugBreak                                equ @RhDebugBreak@0
RhpGcAlloc                                  equ @RhpGcAlloc@16
G_LOWEST_ADDRESS                            equ _g_lowest_address
G_HIGHEST_ADDRESS                           equ _g_highest_address
G_EPHEMERAL_LOW                             equ _g_ephemeral_low
G_EPHEMERAL_HIGH                            equ _g_ephemeral_high
G_CARD_TABLE                                equ _g_card_table
RhpWaitForGC2                               equ @RhpWaitForGC2@4
RhpTrapThreads                              equ _RhpTrapThreads
RhpStressGc                                 equ @RhpStressGc@0
RhpGcPoll2                                  equ @RhpGcPoll2@4
RhHandleGet                                 equ @RhHandleGet@4
RhpGcSafeZeroMemory                         equ @RhpGcSafeZeroMemory@8
RhpGetNumThunkBlocksPerMapping              equ @RhpGetNumThunkBlocksPerMapping@0

ifdef FEATURE_GC_STRESS
THREAD__HIJACKFORGCSTRESS                   equ ?HijackForGcStress@Thread@@SGXPAUPAL_LIMITED_CONTEXT@@@Z
endif ;; FEATURE_GC_STRESS

;;
;; IMPORTS
;;
EXTERN RhpGcAlloc                               : PROC
EXTERN RhDebugBreak                             : PROC
EXTERN RhpWaitForGC2                            : PROC
EXTERN RhExceptionHandling_FailedAllocation     : PROC
EXTERN RhThrowHwEx                              : PROC
EXTERN RhThrowEx                                : PROC
EXTERN RhRethrow                                : PROC
EXTERN RhpGcPoll2                               : PROC

;; The following imports are not used in the assembly helpers. Due to the
;; way the FCall mangling is handled in the C sources through linker directives
;; (see FCIMPL macro definitions in CommonMacros.h), we need to add dummy
;; references to some methods in few object files (HandleTableHelpers and
;; GCMemoryHelpers, ThunkMapping) to get the linker to see the #pragma
;; comment(linker, ...) directives embedded in those files.
EXTERN RhHandleGet                              : PROC
EXTERN RhpGcSafeZeroMemory                      : PROC
EXTERN RhpGetNumThunkBlocksPerMapping           : PROC

ifdef FEATURE_GC_STRESS
EXTERN THREAD__HIJACKFORGCSTRESS                : PROC
EXTERN RhpStressGc                              : PROC
endif ;; FEATURE_GC_STRESS

EXTERN G_LOWEST_ADDRESS : DWORD
EXTERN G_HIGHEST_ADDRESS : DWORD
EXTERN G_EPHEMERAL_LOW : DWORD
EXTERN G_EPHEMERAL_HIGH : DWORD
EXTERN G_CARD_TABLE : DWORD
EXTERN RhpTrapThreads : DWORD
